package nio.common;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.alibaba.fastjson.JSON;
import com.sun.corba.se.spi.orbutil.fsm.Guard.Result;

import nio.entity.User;

public class SocketChannelDemo {
	static String result1 = "";
	static String result2 = "";
	// static Selector selector;
	// static {
	// try {
	// selector = Selector.open();
	// } catch (IOException e) {
	// e.printStackTrace();
	// }
	// }

	public static void main(String[] args) {
//		socketChannelTest();
		List<Map<String, Object>> urlInfoList = new ArrayList<>();
		Map<String, Object> map = new HashMap<>();
		map.put("port", 9998);
		map.put("ip", "127.0.0.1");
		urlInfoList.add(map);
		List<String> responseList = new ArrayList<>();
		new Thread(new Runnable() {
			public void run() {
				NioSocketRequest(urlInfoList,responseList);
			}
		}).start();
		try {
			Thread.sleep(1000);
			System.out.println(responseList.get(0));
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public static List<String> NioSocketRequest(List<Map<String, Object>> urlInfoList,List<String> responseList) {
		Map<Integer, String> resultMap = new HashMap<>();
		Selector selector = null;
		try {
			selector = Selector.open();
		} catch (IOException e1) {
			e1.printStackTrace();
		}
		int attachmentNumber = 0;
		for (Map<String, Object> map : urlInfoList) {
			String IP = (String) map.get("ip");
			int port = (int) map.get("port");
			try {
				SocketChannel socketChannel = SocketChannel.open();
				socketChannel.configureBlocking(false);
				InetSocketAddress inetSocketAddress = new InetSocketAddress(IP, port);
				socketChannel.connect(inetSocketAddress);
				socketChannel.register(selector, SelectionKey.OP_CONNECT, ++attachmentNumber);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}
		process(selector, resultMap,responseList);
		return responseList;
	}

	public static void socketChannelTest() {
		SocketChannel socketChannel;
		SocketChannel socketChannel2;
		try {
			socketChannel = SocketChannel.open();
			socketChannel2 = SocketChannel.open();
			socketChannel.configureBlocking(false);
			socketChannel2.configureBlocking(false);
			InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 9998);
			InetSocketAddress inetSocketAddress2 = new InetSocketAddress("127.0.0.1", 9999);
			socketChannel.connect(inetSocketAddress);
			// socketChannel2.connect(inetSocketAddress2);
			Selector selector = Selector.open();
			socketChannel.register(selector, SelectionKey.OP_CONNECT, 1);
			// socketChannel2.register(selector, SelectionKey.OP_CONNECT, 2);
			process(selector,new HashMap<>(),new ArrayList<>());
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static void process(Selector selector, Map<Integer, String> resultMap,List<String> responseList) {
		try {
			long start = 0l;
			boolean isCheckd = false;
			boolean isWrited = false;
			List<Integer> rpsLengthList = new ArrayList<>();
			Integer rps1Length = -1;
			Integer rps2Length = -1;
			rpsLengthList.add(rps1Length);
			rpsLengthList.add(rps2Length);
			while (true) {
				System.out.println("result1:" + result1);
				System.out.println("result2:" + result2);
				int readyChannels = selector.select(1000);
				System.out.println("readyChannels is:" + readyChannels);
				if (readyChannels == 0) {
					if (isCheckd == false) {
						isCheckd = true;
						start = System.currentTimeMillis();
						// } else if (isCheckd == true &&
						// System.currentTimeMillis() - start > 2000) {
						// selector.close();
						// System.out.println("selector is closed!");
						// System.out.println("result1:" + result1);
						// System.out.println("result2:" + result2);
						// break;
					}
					continue;
				}
				Set<SelectionKey> selectedKeys = selector.selectedKeys();
				System.out.println(selectedKeys.size());
				Iterator<SelectionKey> iterator = selectedKeys.iterator();

				while (iterator.hasNext()) {
					SelectionKey selectionKey = iterator.next();
					System.out.println("attachment:" + selectionKey.attachment());
					int attachment = (int) selectionKey.attachment();
					if (selectionKey.isAcceptable()) {
						// a connection was accepted by a ServerSocketChannel.
					} else if (selectionKey.isConnectable()) {
						// a connection was established with a remote server.
						SocketChannel channel = (SocketChannel) selectionKey.channel();
						System.out.println("isBlocking:" + channel.isBlocking());
						if (channel.isConnectionPending()) {
							channel.finishConnect();
						}
						String userJStr = "";
						if ((int) selectionKey.attachment() == 1) {
							userJStr = JSON.toJSONString(new User("Tom", "Man", 25));
							channel.write(ByteBuffer.wrap(userJStr.getBytes()));
							channel.register(selector, SelectionKey.OP_READ, 1);
						} else {
							userJStr = JSON.toJSONString(new User("Liming", "Man", 24));
							channel.write(ByteBuffer.wrap(userJStr.getBytes()));
							channel.register(selector, SelectionKey.OP_READ, 2);
						}
					} else if (selectionKey.isReadable()) {
						SocketChannel channel = (SocketChannel) selectionKey.channel();
						System.out.println("======reading=====");
						ByteBuffer buffer = ByteBuffer.allocate(20);
						channel.read(buffer);
						buffer.flip();
						while (buffer.hasRemaining()) {
							if (attachment == 1) {
								result1 += (char) buffer.get();
							} else if (attachment == 2) {
								result2 += (char) buffer.get();
							}
						}
						if (attachment == 1) {
							if (rpsLengthList.get(0) == null || rpsLengthList.get(0).intValue() == -1) {
								rpsLengthList.set(0, getMessageLengthFromResponse(result1));
								System.out.println(rpsLengthList.get(0).intValue());
								if (rpsLengthList.get(0).intValue() == result1.length()) {
									responseList.add(result1);
									resultMap.put(attachment, result1);
									selectionKey.cancel();
								}
							} 
						} else if (attachment == 2) {
							if (rpsLengthList.get(1) == null || rpsLengthList.get(1).intValue() == -1) {
								rpsLengthList.set(1, getMessageLengthFromResponse(result2));
								System.out.println(rpsLengthList.get(1).intValue());
								if (rpsLengthList.get(1).intValue() == result2.length()) {
									responseList.add(result2);
									resultMap.put(attachment, result2);
									selectionKey.cancel();
								}
							}
						}
						buffer.clear();
						System.out.println("======reading end======");
					} else if (selectionKey.isWritable()) {
						// a channel is ready for writing
					}
					iterator.remove();
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static Integer getMessageLengthFromResponse(String result) {
		Integer length = new Integer(-1);
		if (result != null && !result.equals("")) {
			int endLocation = result.indexOf("#");
			int startLocation = result.indexOf("ML");
			if (startLocation != -1 && endLocation != -1 && startLocation < endLocation) {
				length = Integer.valueOf((result.substring(startLocation + 2, endLocation)));
			}
		}
		return length;
	}

	public static long attachmentMaker() {
		String randomPart = "";
		for (int i = 0; i < 5; i++) {
			randomPart += String.valueOf((int) Math.random() * 10);
		}
		String systemMillionTime = String.valueOf(System.currentTimeMillis());
		return Long.valueOf(randomPart + systemMillionTime);
	}

	public static void selectorStaticTest() {
		try {
			SocketChannel socketChannel2 = SocketChannel.open();
			socketChannel2.configureBlocking(false);
			InetSocketAddress inetSocketAddress2 = new InetSocketAddress("127.0.0.1", 9999);
			socketChannel2.connect(inetSocketAddress2);
			socketChannel2.register(selector, SelectionKey.OP_CONNECT, 2);
			Thread.sleep(10000000);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
